home *** CD-ROM | disk | FTP | other *** search
/ The PC-SIG Library 10 / The PC-Sig Library - Shareware for the IBM PC and Compatibles (PC-SIG)(Tenth Edition Disks 1-2804)(1991).iso / PC_SIGCD / 02 / 0 / DISK0202.ZIP / LPT_PKG.ASM < prev    next >
Assembly Source File  |  1984-01-01  |  16KB  |  445 lines

  1.     INCLUDE    C:TITLE.MAC
  2.     .TITLE    <LPT_PKG -- LPT1: Routines for Lattice C>
  3.     .SBTTL    <History and Copyright Notice>
  4.  
  5. ; lpt_pkg.asm  18 Nov 83  Craig Milo Rogers at USC/ISI
  6. ;    Use int_pkg routines to set/restore interrupt vectors.
  7. ; lpt_pkg.asm  16 Nov 83  Craig Milo Rogers at USC/ISI
  8. ;    Converted to PDP-11-like TITLEs.
  9. ;    Converted to STRUCT for control data.
  10. ; lpt_pkg.asm  9 Nov 83  Craig Milo Rogers at USC/ISI
  11. ;    Created from com_pkg.asm.
  12. ;
  13. ;    These routines provide an interrupt-driven circular buffer
  14. ; interface to the LPT1: device.  This version interfaces with the
  15. ; multi-model Lattice C compiler version 1.05.  This package was
  16. ; adapted from the "com_pkg", which in turn was adapted from "COMM-PKG".
  17. ; See below:
  18. ;
  19. ; COM_PKG1 provides a library of serial port routines
  20. ; Adapted from code by John Romkey and Jerry Saltzer of MIT
  21. ; by Richard Gillmann (GILLMANN@ISIB), 1983
  22. ;
  23.  
  24.     .SBHED    Overview
  25.  
  26. ;    This is a module of routines for interfacing with the
  27. ; LPT1: communications interface on the IBM PC.  The code has
  28. ; been carefully constructed to properly drive the printer interface
  29. ; and the 8259 Interrupt Controller.  Internal circular buffers
  30. ; are used for transmit and receive.
  31.  
  32. ;    Only one LPT: is supported at present.  A unit number is
  33. ; included in the calls to provide for multiple-printer support in
  34. ; the future.
  35.  
  36. ;    The LPT: interrupt source is the -ACKNOWLEGE signal from
  37. ; the printer.  In the case of the Epson MX and FX printers, -ACKNOWLEGE
  38. ; goes active (low) for 5 usec when a character has been processed,
  39. ; then returns to inactive (high).  Since the -ACKNOWLEGE signal is
  40. ; inverted by the printer interface card before it is presented to
  41. ; the IRQ7 line on the IBM-PC's bus, it is the high-to-low transition
  42. ; of -ACKNOWLEGE which causes the low-to-high transition of IRQ7, which
  43. ; in turn triggers the interrupt sequence in the 8259.  This, in turn,
  44. ; triggers an interrupt sequence in the 8088 processor.
  45. ;
  46. ;    Thus, it is the high-to-low transition of -ACKNOWLEGE which
  47. ; starts the interrupt sequence.  However, since -ACKNOWLEGE is low
  48. ; for only 5 usec, it may have returned to its high state before the
  49. ; 8088 sends INTA to the 8259 to acknowlege the interrupt.  This is
  50. ; a violation of the 8259 specification.  The 8259 will then generate
  51. ; a "DEFAULT" interrupt request cycle, instead of a normal IR7 cycle.
  52. ; However, since the DEFAULT cycle just happens to be IR7, too, it
  53. ; all works out OK.
  54. ;
  55. ;    The 8259's restriction on IRn pulse length is intended to
  56. ; catch static on the IRn lines, and/or malfunctioning devices.
  57. ; It is entirely possible that a DEFAULT IR7 may be generated for
  58. ; some reason other than the line printer.  The line printer interrupt
  59. ; routine attempts to protect against this case by checking the
  60. ; BUSY bit in the interface.  Everything would probably work a lot
  61. ; better if the line printer interface fed the -ACKNOWLEGE signal
  62. ; directly to IR7, instead of inverting it first.
  63.  
  64. ;    Entry points (Lattice C 1.05 calling conventions):
  65.  
  66. ; void
  67. ; lpt_ini(unit, tbuf, tbuflen, pinit)
  68. ;            /* Initializes port and interrupt vector. */
  69. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  70. ; char *tbuf;        /* Transmit buffer address. */
  71. ; int tbuflen;        /* Transmit buffer length. */
  72. ; bool pinit;        /* TRUE ==> force printer initialization. */
  73.  
  74. ; void
  75. ; lpt_trm(unit)        /* Turns off interrupts from the aux port. */
  76. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  77.  
  78. ; int            /* Number of free bytes in output buffer. */
  79. ; lpt_ocnt(unit)    /* Returns number of free bytes in output buffer. */
  80. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  81.  
  82. ; bool            /* Returns FALSE if no more room. */
  83. ; lpt_putc(unit, ch)    /* Writes a character to the output buffer. */
  84. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  85. ; char ch;        /* The character to write. */
  86.  
  87. ; int            /* Returns printer status bits. */
  88. ; lpt_stat(unit)    /* Reads printer hardware status. */
  89. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  90.  
  91.  
  92.     .SBHED    Declarations
  93.  
  94. IF1
  95.     INCLUDE    DOS.MAC        ; C segments.
  96.     INCLUDE BMAC.MAC    ; C calling conventions.
  97. ENDIF
  98.  
  99.                 ; int_pkg routines:
  100.     BEXTRN    INT_SETU    ; Setup an interrupt vector.
  101.     BEXTRN    INT_REST    ; Restore an interrupt vector.
  102.  
  103.                 ; LPT: parameters:
  104. LPT_INT      EQU     7        ; Interrupt number for printer port.
  105. PRINTER_BASE EQU     408H    ; Address of BIOS table containing
  106.                 ; the addresses of the printers.
  107.  
  108. INT_OFF EQU     08H        ; Converts 8029 interrupt numbers to
  109.                 ; 8088 interrupt numbers.
  110.  
  111.                 ; Printer device registers:
  112. DATREG  EQU     0H        ; Data register.
  113. STATREG EQU    1H        ; Status bits from the printer.
  114. CMDREG    EQU    2H        ; Command bits to the printer. 
  115.  
  116.                 ; Printer status bits:
  117. BUSY    EQU    80H        ; Printer is busy, issue no commands.
  118. ACK    EQU    40H        ; Printer ready acknowlegement pulse.
  119. PAPER    EQU    20H        ; Paper end -- out of paper.
  120. SELECTD    EQU    10H        ; Printer is selected.
  121. ERR    EQU    08H        ; Error line.
  122.  
  123.                 ; Printer command bits:
  124. IRQE    EQU    10H        ; Interrupt request enable.
  125. SELECT    EQU    08H        ; Select input to printer.
  126. INIT    EQU    04H        ; Initialize printer.
  127. AUTOF    EQU    02H        ; Auto line feed after carriage return.
  128. STROBE    EQU    01H        ; Data strobe pulse.
  129.  
  130.                 ; 8259 interrupt controller:
  131. IMR    EQU    21H        ; Interrupt mask register.
  132. OCW2    EQU     20H             ; Operational control word.
  133. EOI     EQU     60H        ; Specific end of interrupt.
  134.  
  135.                 ; Interface to C language
  136. TRUE    EQU     1               ; Truth.
  137. FALSE   EQU     0               ; Falsehood.
  138.  
  139.  
  140. LPTX_CTRL    STRUC        ; Line printer control structure:
  141.  
  142. TBUF_SEG    DW    ?    ; Transmit buffer segment number.
  143. TBUF_OFF    DW    ?    ; Transmit buffer offset.
  144. TBUF_SIZE    DW    ?    ; Transmit buffer size.
  145.  
  146. START_TDATA     DW      ?       ; Index to first character in x-mit buffer.
  147. END_TDATA       DW      ?       ; Index to first free space in x-mit buffer.
  148. SIZE_TDATA      DW      ?       ; Number of characters in x-mit buffer.
  149.  
  150. LPTX_BASE    DW    ?    ; I/O base address of printer registers.
  151.  
  152. LPTX_CTRL    ENDS        ; End of the LPT control structure.
  153.  
  154.     .SBHED    <Data Storage>
  155.  
  156.     DSEG
  157.  
  158. LPT1_CTRL    LPTX_CTRL <>    ; Control parameters for LPT1:
  159.  
  160.     ENDDS
  161.  
  162.     PSEG            ; All the rest is code.
  163.  
  164.     .SBHED    <LPT: Interrupt Handler>
  165. ;
  166. ; INT_HNDLR - Handles Interrupts Generated by LPT:
  167. ;
  168. ;    WARNING!
  169. ;    Note the impure use of DATASEG below.  This code is not ROMmable.
  170. ;
  171. ;    There is no provision for recovery in the face of printer errors.
  172. ;
  173. DATASEG DW      0        ; Holds our data segment number.
  174.  
  175. INT_HNDLR PROC  FAR        ;;; Enter here on interrupt.
  176.         PUSH    DS        ;;; Save data segment register.
  177.         PUSH    CS:DATASEG    ;;; Set up new data segment.
  178.         POP    DS        ;;;
  179.  
  180.         PUSH    ES        ;;; Save previous context on existing stack.
  181.         PUSH    BP        ;;;
  182.         PUSH    SI        ;;;
  183.         PUSH    DI        ;;;
  184.         PUSH    AX        ;;;
  185.         PUSH    BX        ;;;
  186.         PUSH    CX        ;;;
  187.         PUSH    DX        ;;;
  188.  
  189.     MOV    SI,OFFSET LPT1_CTRL ;;; Setup pointer to control structure.
  190.  
  191.                 ;;; Clear the interrupt request first
  192.                 ;;; so new request pulses will not
  193.                 ;;; be ignored.
  194.         MOV     DX,OCW2         ;;; Tell the 8259 that I'm done.
  195.         MOV     AL,EOI        ;;; Get the End-of-Interrupt code.
  196.     OR    AL,LPT_INT    ;;; Set to specific int. number.
  197.         OUT     DX,AL        ;;;
  198.  
  199. REPOLL:
  200.     MOV    DX,[SI].LPTX_BASE    ;;; Get LPT: base register.
  201.         ADD    DX,CMDREG    ;;; Point to command bits.
  202.         IN      AL,DX        ;;; Read the command bits.
  203.         TEST    AL,IRQE        ;;; Is interrupt request enable on?
  204.          JZ     INT_END        ;;; No, return from interrupt.
  205.     ADD    DX,(STATREG-CMDREG)    ;;; Point to status bits.
  206.     IN    AL,DX        ;;; Read the status bits.
  207.     TEST    AL,BUSY        ;;; Is the printer still busy?
  208.      JZ    INT_END        ;;; Yes, ignore this interrupt.
  209.  
  210. GOODTX: CMP     [SI].SIZE_TDATA,0    ;;; See if any more data to send.
  211.      JNE    HAVE_DATA       ;;; If not equal then there is data to send.
  212.  
  213. ;;; If no data to send then reset tx interrupt and return.
  214.         ADD    DX,(CMDREG-STATREG) ;;;
  215.         MOV     AL,(SELECT+INIT) ;;;
  216.         OUT     DX,AL        ;;;
  217.         JMP SHORT INT_END    ;;;
  218.  
  219. HAVE_DATA:
  220.     MOV    ES,[SI].TBUF_SEG ;;; Get transmit buffer segment number.
  221.     MOV    DI,[SI].TBUF_OFF ;;; Get transmit buffer offset.
  222.         MOV     BX,[SI].START_TDATA  ;;; BX points to next char. to be sent.
  223.     MOV    DX,[SI].LPTX_BASE    ;;;
  224.         ADD    DX,DATREG       ;;; DX equals port to send data to.
  225.         MOV     AL,ES:[BX+DI]   ;;; Get data from buffer.
  226.         OUT     DX,AL           ;;; Put data in output register.
  227.     ADD    DX,(CMDREG-DATREG) ;;; Point to command register.
  228.         MOV     AL,(IRQE+SELECT+INIT+STROBE) ;;; Prepare to strobe data.
  229.     OUT    DX,AL        ;;; Set strobe high.
  230.         MOV     AL,(IRQE+SELECT+INIT) ;;;
  231.     OUT    DX,AL        ;;; Set strobe low.
  232.         INC     BX              ;;; Increment START_TDATA.
  233.         CMP     BX,[SI].TBUF_SIZE    ;;; See if gone past end.
  234.      JB    NTADJ           ;;; If not then skip.
  235.         XOR     BX,BX        ;;; Reset to beginning.
  236. NTADJ:  MOV     [SI].START_TDATA,BX  ;;; Save START_TDATA.
  237.         DEC     [SI].SIZE_TDATA      ;;; One less character in x-mit buffer.
  238.     JMP    REPOLL        ;;; Check again is ready for next char.
  239.  
  240. INT_END:
  241.         POP     DX        ;;; Restore previous context.
  242.         POP     CX        ;;;
  243.         POP     BX        ;;;
  244.         POP     AX        ;;;
  245.         POP     DI        ;;;
  246.         POP     SI        ;;;
  247.         POP     BP        ;;;
  248.         POP     ES        ;;;
  249.         POP     DS        ;;;
  250.         IRET            ;;; Return from interrupt.
  251.  
  252. INT_HNDLR ENDP
  253.  
  254.     .SBHED    <LPT_INI -- Initialize Communication Port>
  255.  
  256. ; void
  257. ; lpt_ini(unit, tbuf, tbuflen, pinit)
  258. ;            /* Initializes port and interrupt vector. */
  259. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  260. ; char *tbuf;        /* Transmit buffer address. */
  261. ; int tbuflen;        /* Transmit buffer length. */
  262. ; bool pinit;        /* TRUE ==> force printer initialization. */
  263.  
  264. ;    Initialize the Intel 8250 and set up interrupt vector to int_hndlr.
  265.  
  266. IF LDATA
  267.     BENTRY    LPT_INI    <UNIT,TBOFF,TBSEG,TBLEN,PINIT>
  268. ELSE
  269.     BENTRY    LPT_INI    <UNIT,TBOFF,TBLEN,PINIT>
  270. ENDIF
  271.  
  272.         MOV     AX,DS        ; Copy our data segment number.
  273. IFE LDATA
  274.     MOV    ES,AX        ; Save for buffer addresses.
  275. ENDIF
  276.         MOV     CS:DATASEG,AX    ; Store segment # in code space (gulp!).
  277.  
  278.     MOV    SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
  279.  
  280.                 ; Pickup printer port from BIOS:
  281.         PUSH    DS        ; Save current data segment.
  282.         XOR     AX,AX        ; Zero AX.
  283.         MOV     DS,AX        ; Switch to segment zero.
  284.     MOV    AX,WORD PTR DS:PRINTER_BASE ; Get the printer port.
  285.     POP    DS        ; Restore our data segment.
  286.     MOV    [SI].LPTX_BASE,AX    ; Save printer base address.
  287.  
  288. IF LDATA
  289.     MOV    AX,TBSEG    ; Get the transmit buffer segment number.
  290. ELSE
  291.     MOV    AX,ES        ; Default transmit buffer segment number.
  292. ENDIF
  293.     MOV    [SI].TBUF_SEG,AX    ; Save it.
  294.     MOV    AX,TBOFF    ; Copy the transmit buffer offset.
  295.     MOV    [SI].TBUF_OFF,AX    ;
  296.     MOV    AX,TBLEN    ; Copy the transmit buffer length.
  297.     MOV    [SI].TBUF_SIZE,AX    ;
  298.  
  299.     XOR    AX,AX        ; Clear the accumulator.
  300.     MOV    [SI].START_TDATA,AX    ; Reset start of transmitted data.
  301.     MOV    [SI].END_TDATA,AX    ; Reset end of transmitted data.
  302.     MOV    [SI].SIZE_TDATA,AX    ; Reset number of transmitted chars.
  303.  
  304.     CMP    WORD PTR PINIT,0    ; Do we want a printer init?
  305.      JE    NOINIT        ;   (nope)
  306.     MOV    DX,[SI].LPTX_BASE    ; Get printer base address.
  307.     ADD    DX,CMDREG    ; Point to command register.
  308.         MOV     AL,(SELECT)
  309.         OUT     DX,AL        ; Start initialization.
  310.  
  311.     MOV    AX,1000        ; Prepare to burn some time.
  312. INILOP:    DEC    AX
  313.     JNZ    INILOP
  314.  
  315.         MOV     AL,(SELECT+INIT)
  316.         OUT     DX,AL        ; Stop initialization.
  317. NOINIT:
  318.  
  319.                 ; Setup the LPT interrupt vector:
  320.     MOV    AX,(LPT_INT+INT_OFF)    ; Get the LPT: interrupt number.
  321.     MOV    BX,OFFSET INT_HNDLR    ; Start of the interrupt routine.
  322.     BCALL    INT_SETU <AX BX CS>    ; Call int_setup(vec, newip, newcs).
  323.  
  324.         CLI            ; ******* Disable Interrupts *******
  325.  
  326.                 ;;; Enable interrupts on 8259:
  327.         IN      AL,IMR          ;;; Get current enable bits on 8259.
  328.     MOV    CL,LPT_INT    ;;; Get interrupt number.
  329.     MOV    BL,1        ;;; Convert to
  330.     SHL    BL,CL        ;;;   bit position.
  331.     NOT    BL        ;;; Clear current
  332.         AND     AL,BL        ;;;   interrupt bit.
  333.         OUT     IMR,AL        ;;; Set enable on 8259.
  334.  
  335.         STI            ;;; ******* Enable Interrupts *******
  336.                 ;;; (Next instruction still disabled)
  337.     BEND    LPT_INI
  338.  
  339.     .SBHED    <LPT_TRM -- Turn Off Interrupts and Shutdown>
  340.  
  341. ; void
  342. ; lpt_trm(unit)        /* Turns off interrupts from the LPT: port. */
  343. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  344.  
  345.     BENTRY    LPT_TRM <UNIT>
  346.  
  347.     MOV    SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
  348.  
  349.     MOV    DX,[SI].LPTX_BASE
  350.     ADD    DX,CMDREG    ; Turn off line printer interface.
  351.         MOV     AL,(SELECT+INIT)
  352.         OUT     DX,AL
  353.  
  354.         IN      AL,IMR        ; Turn off 8259 interrupt controller.
  355.     MOV    CL,LPT_INT    ; Get interrupt number.
  356.     MOV    BL,1        ; Convert to
  357.     SHL    BL,CL        ;   bit position.
  358.         OR      AL,BL        ; Disable this interrupt.
  359.         OUT     IMR,AL
  360.  
  361.                 ; Restore the LPT interrupt vector:
  362.     MOV    AX,(LPT_INT+INT_OFF)    ; Get the LPT: interrupt number.
  363.     BCALL    INT_REST <AX>        ; Call int_restore(vec).
  364.  
  365.     BEND    LPT_TRM
  366.  
  367.     .SBHED    <LPT_OCNT -- Returns Number of Free Bytes>
  368.  
  369. ; int            /* Number of free bytes in output buffer. */
  370. ; lpt_ocnt(unit)    /* Returns number of free bytes in output buffer. */
  371. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  372.  
  373.     BENTRY    LPT_OCNT <UNIT>
  374.  
  375.     MOV    SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
  376.  
  377.         MOV     AX,[SI].TBUF_SIZE    ; Get the size of the x-mit buffer.
  378.         SUB     AX,[SI].SIZE_TDATA    ; Subtract the number of bytes used.
  379.  
  380.     BEND    LPT_OCNT
  381.  
  382.     .SBHED    <LPT_PUTC -- Queue a Character for Output>
  383.  
  384. ; bool            /* Returns FALSE if no more room. */
  385. ; lpt_putc(unit, ch)    /* Writes a character to the output buffer. */
  386. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  387. ; char ch;        /* The character to write. */
  388.  
  389.  
  390.     BENTRY    LPT_PUTC <UNIT,OCHAR>
  391.  
  392.     MOV    SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
  393.  
  394.         MOV     AX,[SI].TBUF_SIZE    ; Get the size of the x-mit buffer.
  395.         SUB     AX,[SI].SIZE_TDATA    ; Subtract the number of bytes used.
  396.      JE    L24        ; No more free space.
  397.  
  398.     MOV    ES,[SI].TBUF_SEG    ; Get transmit buffer segment number.
  399.     MOV    DI,[SI].TBUF_OFF    ; Get transmit buffer offset.
  400.         MOV     BX,[SI].END_TDATA    ; BX points to free space.
  401.         MOV     AL,OCHAR        ; Move data from stack to x-mit buffer.
  402.         MOV     ES:[BX+DI],AL
  403.         INC     BX              ; Increment END_TDATA to point to free space.
  404.         CMP     BX,[SI].TBUF_SIZE    ; See if past end.
  405.          JB      L20        ; If not then skip.
  406.         XOR     BX,BX        ; Adjust to beginning.
  407. L20:    MOV     [SI].END_TDATA,BX    ; Save new END_TDATA.
  408.  
  409.         INC     [SI].SIZE_TDATA    ; One more character in x-mit buffer.
  410.     MOV    DX,[SI].LPTX_BASE ; Prepare to manipulate printer interrupts.
  411.         ADD    DX,CMDREG    ; Point to printer command register.
  412.     IN    AL,DX        ; Read command register.
  413.     TEST    AL,IRQE        ; Are printer interrupts enabled?
  414.      JNZ    L22        ; Yes, so output is active.
  415.         MOV     AL,(IRQE+SELECT+INIT) ; No, so enable printer interrupts.
  416.         OUT     DX,AL        ;
  417.     INT    (LPT_INT+INT_OFF) ; Request an interrupt to start output.
  418. L22:
  419.     MOV    AX,TRUE        ; Indicate all's OK.
  420.     JMP SHORT L26        ; Go join common return code.
  421.  
  422. L24:    MOV    AX,FALSE    ; No more space in buffer.
  423.  
  424. L26:    BEND    LPT_PUTC
  425.  
  426.     .SBHED    <LPT_STAT -- Return Line Printer Hardware Status>
  427.  
  428. ; int            /* Returns printer status bits. */
  429. ; lpt_stat(unit)    /* Reads printer hardware status. */
  430. ; int unit;        /* 1 ==> LPT1:, 2 ==> LPT2:. */
  431.  
  432.     BENTRY    LPT_STAT <UNIT>
  433.  
  434.     MOV    SI,OFFSET LPT1_CTRL ; Setup pointer to control structure.
  435.  
  436.     MOV    DX,[SI].LPTX_BASE    ; Get LPT: base register.
  437.         ADD    DX,STATREG    ; Point to status bits.
  438.         IN      AL,DX        ; Read the status bits.
  439.     XOR    AH,AH        ; Clear high bits.
  440.                 ; Return result in AX.
  441.     BEND    LPT_STAT
  442.  
  443.     ENDPS
  444.         END
  445.